// directory.cc 
//	Routines to manage a directory of file names.
//
//	The directory is a table of fixed length entries; each
//	entry represents a single file, and contains the file name,
//	and the location of the file header on disk.  The fixed size
//	of each directory entry means that we have the restriction
//	of a fixed maximum size for file names.
//
//	The constructor initializes an empty directory of a certain size;
//	we use ReadFrom/WriteBack to fetch the contents of the directory
//	from disk, and to write back any modifications back to disk.
//
//	Also, this implementation has the restriction that the size
//	of the directory cannot expand.  In other words, once all the
//	entries in the directory are used, no more files can be created.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "debug.h"
#include "utility.h"
#include "filehdr.h"
#include "directory.h"

//----------------------------------------------------------------------
// Directory::Directory
// 	Initialize a directory; initially, the directory is completely
//	empty.  If the disk is being formatted, an empty directory
//	is all we need, but otherwise, we need to call FetchFrom in order
//	to initialize it from disk.
//
//	"size" is been removed by tankery...
//----------------------------------------------------------------------

Directory::Directory() {
}

//----------------------------------------------------------------------
// Directory::~Directory
// 	De-allocate directory data structure.
//----------------------------------------------------------------------

Directory::~Directory() {
}

//----------------------------------------------------------------------
// Directory::FetchFrom
// 	Read the contents of the directory from disk.
//
//	"file" -- file containing the directory contents
//----------------------------------------------------------------------

void Directory::FetchFrom(OpenFile *file) {
    int size = 0;
    table.clear();
    // first, read the directory size from disk.
    (void) file->ReadAt((char *) &size, 1, 0);
    // then, allocate space for `table'.
    table.resize(divRoundUp(size, sizeof(table[0])));
    // last, read the real data.
    (void) file->ReadAt((char *) table.data(), size, 1);

    DEBUG(dbgFile, "in FetchFrom:\t" << "table start: " << (int) table.data() << "\t"
            << "table size: " << size);
}

//----------------------------------------------------------------------
// Directory::WriteBack
// 	Write any modifications to the directory back to disk
//
//	"file" -- file to contain the new directory contents
//----------------------------------------------------------------------

void Directory::WriteBack(OpenFile *file) {
    int size = table.size() * sizeof(table[0]);
    int length = file->Length();

    // if data larger then file, append file size.
    // the + 1 is for size byte....
    if (size + 1 > length) {
        DEBUG(dbgFile, "in WriteBack:\t" << "append " << size + 1 - length
                << " to file (" << length << " original).");
        if (!file->AppendFileSize(size + 1 - length)) {
            cout << "in WriteBack:\t" << "append failed." << endl;
            return;
        }
    }
    // this is important!
    // this make the change of directoryFile been saved...
    file->WriteBack(Find("./"));

    // first, write the directory size to disk.
    (void) file->WriteAt((char *) &size, 1, 0);
    // then, write the real data.
    (void) file->WriteAt((char *) table.data(), size, 1);

    DEBUG(dbgFile, "in WriteBack:\t" << "table start: " << (int) table.data() << "\t"
            << "table size: " << size);
}

//----------------------------------------------------------------------
// Directory::FindIndex
// 	Look up file name in directory, and return its location in the table of
//	directory entries.  Return -1 if the name isn't in the directory.
//
//	"name" -- the file name to look up
//----------------------------------------------------------------------

int Directory::FindIndex(char *name) {
    for (unsigned int i = 0; i < table.size(); i++)
        if (!strncmp(table[i].name, name, FileNameMaxLen))
            return i;
    return -1; // name not in directory
}

//----------------------------------------------------------------------
// Directory::Find
// 	Look up file name in directory, and return the disk sector number
//	where the file's header is stored. Return -1 if the name isn't
//	in the directory.
//
//	"name" -- the file name to look up
//----------------------------------------------------------------------

int Directory::Find(char *name) {
    int i = FindIndex(name);

    if (i != -1)
        return table[i].sector;
    return -1;
}

int Directory::GetIndex(int sector) {
    for (unsigned int i = 0; i < table.size(); i++)
        if (table[i].sector == sector)
            return i;
    return -1; // name not in directory
}

char *
Directory::Get(int sector) {
    int i = GetIndex(sector);

    if (i != -1)
        return table[i].name;

    return NULL;
}

char *
Directory::GetFirstDirectory() {
    // skip the ./ and ../
    for (unsigned int i = 2; i < table.size(); i++) {
        char * str = table[i].name;
        while (*(str) != '\0') {
            if (*str == '/') {
                return table[i].name;
            }
            str++;
        }
    }
    return NULL; // no more directory.
}

char *
Directory::GetFirstFile() {
    // skip the ./ and ../
    for (unsigned int i = 2; i < table.size(); i++) {
        return table[i].name;
    }
    return NULL; // no more directory.
}

//----------------------------------------------------------------------
// Directory::Add
// 	Add a file into the directory.  Return TRUE if successful;
//	return FALSE if the file name is already in the directory, or if
//	the directory is completely full, and has no more space for
//	additional file names.
//
//	"name" -- the name of the file being added
//	"newSector" -- the disk sector containing the added file's header
//----------------------------------------------------------------------

bool Directory::Add(char *name, int newSector) {
    if (FindIndex(name) != -1)
        return FALSE;

    DirectoryEntry newEntry;
    strncpy(newEntry.name, name, FileNameMaxLen);
    newEntry.sector = newSector;
    table.push_back(newEntry);

    return TRUE;
}

//----------------------------------------------------------------------
// Directory::Remove
// 	Remove a file name from the directory.  Return TRUE if successful;
//	return FALSE if the file isn't in the directory.
//
//	"name" -- the file name to be removed
//----------------------------------------------------------------------

bool Directory::Remove(char *name) {
    int i = FindIndex(name);

    if (i == -1)
        return FALSE; // name not in directory

    table.erase(table.begin() + i);
    return TRUE;
}

//----------------------------------------------------------------------
// Directory::modifyfileName
// 	modify a file name from the directory.  Return TRUE if successful;
//	return FALSE if the file isn't in the directory.
//
//	"name1" -- the old file name
//	"name2" -- the new file name
//----------------------------------------------------------------------
bool Directory::modifyfileName(char *name1, char *name2) {
    int i = FindIndex(name1);

    if (i == -1)
        return FALSE; // name not in directory

    memset(table[i].name, 0, sizeof(table[i].name));

    strncpy(table[i].name, name2, FileNameMaxLen);

    return TRUE;
}

//----------------------------------------------------------------------
// Directory::List
// 	List all the file names in the directory.
//----------------------------------------------------------------------

void Directory::List() {
    for (unsigned int i = 0; i < table.size(); i++)
        printf("%s\n", table[i].name);
}

//----------------------------------------------------------------------
// Directory::Print
// 	List all the file names in the directory, their FileHeader locations,
//	and the contents of each file.  For debugging.
//----------------------------------------------------------------------

void Directory::Print() {
    FileHeader *hdr = new FileHeader;

    printf("Directory contents:\n");
    for (unsigned int i = 0; i < table.size(); i++) {
        printf("Name: %s, Sector: %d\n", table[i].name, table[i].sector);
        hdr->FetchFrom(table[i].sector);
        hdr->Print();
    }
    printf("\n");
    delete hdr;
}
